/*-------------------------------------------------------------------------
 *
 * list.c
 *	  PostgreSQL 通用列表包的实现
 *
 * 请参阅 pg_list.h 中的注释。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/nodes/list.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "nodes/pg_list.h"
#include "port/pg_bitutils.h"
#include "utils/memdebug.h"
#include "utils/memutils.h"


/*
 * 之前的 List 实现，由于它为每个 cons 单元使用单独的 palloc 块，
 * 具有这样的特性：添加或删除列表单元不会移动列表中其他现有单元的存储。
 * 相当多的现有代码依赖于此，通过保留 ListCell 指针
 * 跨越此类操作。此实现中没有这样的保证，因此我们有调试支持，
 * 旨在帮助排除现在已破坏的假设。在构建此文件时定义 DEBUG_LIST_MEMORY_USAGE
 * 会导致 List 操作在每次添加或删除单元格时强制移动列表中的所有单元格。
 * 结合 MEMORY_CONTEXT_CHECKING 和/或 Valgrind，这通常可以暴露
 * 破损的代码。尽管这样做的代价有点高，因为存在比默认构建更多的 palloc
 * 循环和数据复制。
 *
 * 默认情况下，我们在为 Valgrind 构建时启用此功能。
 */
#ifdef USE_VALGRIND
#define DEBUG_LIST_MEMORY_USAGE
#endif

/* List 头部的固定部分的开销，以 ListCells 为单位测量 */
#define LIST_HEADER_OVERHEAD  \
	((int) ((offsetof(List, initial_elements) - 1) / sizeof(ListCell) + 1))

/*
 * 简化对列表类型的断言编写的宏；一个 NIL 列表被视为任何类型的空列表。
 */
#define IsPointerList(l)		((l) == NIL || IsA((l), List))
#define IsIntegerList(l)		((l) == NIL || IsA((l), IntList))
#define IsOidList(l)			((l) == NIL || IsA((l), OidList))

#ifdef USE_ASSERT_CHECKING
/*
 * 检查指定的 List 是否有效（就我们能判断的而言）。
 */
static void fc_check_list_invariants(const List *fc_list)
{
	if (fc_list == NIL)
		return;

	Assert(fc_list->length > 0);
	Assert(fc_list->length <= fc_list->max_length);
	Assert(fc_list->elements != NULL);

	Assert(fc_list->type == T_List ||
		   fc_list->type == T_IntList ||
		   fc_list->type == T_OidList);
}
#else
#define fc_check_list_invariants(l)  ((void) 0)
#endif							/* USE_ASSERT_CHECKING */

/*
 * 返回一个新分配的 List，至少有 min_size 单元的空间。
 *
 * 由于空的非 NIL 列表是无效的，new_list() 将初始长度设置为
 * min_size，有效地标记该数量的单元格为有效；调用者负责填充它们的数据。
 */
static List * fc_new_list(NodeTag fc_type, int fc_min_size)
{
	List	   *fc_newlist;
	int			fc_max_size;

	Assert(fc_min_size > 0);

	/*
	 * 我们分配所有请求的单元格，以及可能更多，作为同一 palloc 请求的一部分
	 * 作为列表头。这对于短固定长度列表的典型情况非常有利。
	 * 如果我们分配一个适度长的列表然后再扩展，就会失去效果；
	 * 我们将浪费比如果将头部做小更大的 initial_elements[] 空间。
	 * 然而，正如我们在正常代码路径中所做的那样，上调请求提供了一些
	 * 对小扩展的防御。
	 */

#ifndef DEBUG_LIST_MEMORY_USAGE

	/*
	 * 通常，我们设置一个包含一些额外单元的列表，以便允许它在不进行重新分配的情况下增长。
	 * 更倾向于选择使总分配为2的幂的单元计数，因为 palloc 最终会将其向上舍入到该值。
	 * （对于非常大的分配，这不再成立，但非常长的列表并不常见，
	 * 所以在这种情况下似乎没有必要特殊处理。）
	 *
	 * 最小分配为 8 个 ListCell 单元，根据机器的字宽提供 4 或 5 个可用的 ListCells。
	 * 考虑到 palloc 的开销，这与旧实现中的一个单元列表使用的空间相同，
	 * 对于任何更长的列表则使用更少的空间。
	 *
	 * 我们不必担心整数溢出；没有调用者传递的 min_size 超过现有列表的两倍，
	 * 因此 palloc 内的大小限制将确保我们在这里不会溢出。
	 */
	fc_max_size = pg_nextpower2_32(Max(8, fc_min_size + LIST_HEADER_OVERHEAD));
	fc_max_size -= LIST_HEADER_OVERHEAD;
#else

	/*
	 * 出于调试目的，禁止任何额外空间。这强制任何单元
	 * 添加都必须通过 enlarge_list() 来处理，从而移动现有数据。
	 */
	fc_max_size = fc_min_size;
#endif

	fc_newlist = (List *) palloc(offsetof(List, initial_elements) +
							  fc_max_size * sizeof(ListCell));
	fc_newlist->type = fc_type;
	fc_newlist->length = fc_min_size;
	fc_newlist->max_length = fc_max_size;
	fc_newlist->elements = fc_newlist->initial_elements;

	return fc_newlist;
}

/*
 * 扩展现有的非 NIL 列表，使其至少有 min_size 个单元的空间。
 *
 * 这*不*更新 list->length，因为一些调用者会发现这很不方便。
 * （不过，list->length 应该是现有有效单元的正确数量。）
 */
static void fc_enlarge_list(List *fc_list, int fc_min_size)
{
	int			fc_new_max_len;

	Assert(fc_min_size > fc_list->max_length);	/* 否则我们不应该在这里 */

#ifndef DEBUG_LIST_MEMORY_USAGE

	/*
	 * 如上所述，我们更倾向于分配2的幂的总量；但在这里我们不必考虑列表头的开销。
	 */

	/* 将最小值限制为 16，一个半任意的小2的幂 */
	fc_new_max_len = pg_nextpower2_32(Max(16, fc_min_size));

#else
	/* 如上所述，不分配任何额外空间 */
	fc_new_max_len = fc_min_size;
#endif

	if (fc_list->elements == fc_list->initial_elements)
	{
		/*
		 * 用一个单独的 palloc 块替换原来的内联分配。
		 * 确保它在与 List 头相同的内存上下文中。（以前的 List 实现并未提供
		 * 保证将所有列表单元保持在同一上下文中的任何保证，但现在创建这样的保证似乎是合理的。）
		 */
		fc_list->elements = (ListCell *)
			MemoryContextAlloc(GetMemoryChunkContext(fc_list),
							   fc_new_max_len * sizeof(ListCell));
		memcpy(fc_list->elements, fc_list->initial_elements,
			   fc_list->length * sizeof(ListCell));

		/*
		 * 我们不能移动列表头，因此尝试通过 repalloc 回收 initial_elements[] 空间是不安全的。
		 * 然而，在调试构建中，我们可以清除该空间和/或将其标记为不可访问。
		 * （wipe_mem 包含 VALGRIND_MAKE_MEM_NOACCESS。）
		 */
#ifdef CLOBBER_FREED_MEMORY
		wipe_mem(fc_list->initial_elements,
				 fc_list->max_length * sizeof(ListCell));
#else
		VALGRIND_MAKE_MEM_NOACCESS(list->initial_elements,
								   list->max_length * sizeof(ListCell));
#endif
	}
	else
	{
#ifndef DEBUG_LIST_MEMORY_USAGE
		/* 通常让 repalloc 处理扩展 */
		fc_list->elements = (ListCell *) repalloc(fc_list->elements,
											   fc_new_max_len * sizeof(ListCell));
#else
		/*
		 * repalloc() 可能会就地扩展空间，这在调试时我们不想要，
		 * 因此强制将数据移动到其他地方。
		 */
		ListCell   *fc_newelements;

		fc_newelements = (ListCell *)
			MemoryContextAlloc(GetMemoryChunkContext(fc_list),
							   fc_new_max_len * sizeof(ListCell));
		memcpy(fc_newelements, fc_list->elements,
			   fc_list->length * sizeof(ListCell));
		pfree(fc_list->elements);
		fc_list->elements = fc_newelements;
#endif
	}

	fc_list->max_length = fc_new_max_len;
}

/*
 * 方便函数，用于从给定值构造短列表。
 * （这些通常通过 list_makeN 宏调用。）
 */
List * list_make1_impl(NodeTag fc_t, ListCell fc_datum1)
{
	List	   *fc_list = fc_new_list(fc_t, 1);

	fc_list->elements[0] = fc_datum1;
	fc_check_list_invariants(fc_list);
	return fc_list;
}

List * list_make2_impl(NodeTag fc_t, ListCell fc_datum1, ListCell fc_datum2)
{
	List	   *fc_list = fc_new_list(fc_t, 2);

	fc_list->elements[0] = fc_datum1;
	fc_list->elements[1] = fc_datum2;
	fc_check_list_invariants(fc_list);
	return fc_list;
}

List * list_make3_impl(NodeTag fc_t, ListCell fc_datum1, ListCell fc_datum2,
				ListCell fc_datum3)
{
	List	   *fc_list = fc_new_list(fc_t, 3);

	fc_list->elements[0] = fc_datum1;
	fc_list->elements[1] = fc_datum2;
	fc_list->elements[2] = fc_datum3;
	fc_check_list_invariants(fc_list);
	return fc_list;
}

List * list_make4_impl(NodeTag fc_t, ListCell fc_datum1, ListCell fc_datum2,
				ListCell fc_datum3, ListCell fc_datum4)
{
	List	   *fc_list = fc_new_list(fc_t, 4);

	fc_list->elements[0] = fc_datum1;
	fc_list->elements[1] = fc_datum2;
	fc_list->elements[2] = fc_datum3;
	fc_list->elements[3] = fc_datum4;
	fc_check_list_invariants(fc_list);
	return fc_list;
}

List * list_make5_impl(NodeTag fc_t, ListCell fc_datum1, ListCell fc_datum2,
				ListCell fc_datum3, ListCell fc_datum4, ListCell fc_datum5)
{
	List	   *fc_list = fc_new_list(fc_t, 5);

	fc_list->elements[0] = fc_datum1;
	fc_list->elements[1] = fc_datum2;
	fc_list->elements[2] = fc_datum3;
	fc_list->elements[3] = fc_datum4;
	fc_list->elements[4] = fc_datum5;
	fc_check_list_invariants(fc_list);
	return fc_list;
}

/*
 * 在给定的（非 NIL）列表中为新的头单元腾出空间。
 *
 * 新头单元中的数据未定义；调用者应确保填充它
 */
static void fc_new_head_cell(List *fc_list)
{
	/* 如有必要扩展数组 */
	if (fc_list->length >= fc_list->max_length)
		fc_enlarge_list(fc_list, fc_list->length + 1);
	/* 现在将现有数据移动过来 */
	memmove(&fc_list->elements[1], &fc_list->elements[0],
			fc_list->length * sizeof(ListCell));
	fc_list->length++;
}

/*
 * 在给定的（非 NIL）列表中为新的尾单元腾出空间。
 *
 * 新尾单元中的数据未定义；调用者应确保填充它
 */
static void fc_new_tail_cell(List *fc_list)
{
	/* 如有必要扩展数组 */
	if (fc_list->length >= fc_list->max_length)
		fc_enlarge_list(fc_list, fc_list->length + 1);
	fc_list->length++;
}

/*
 * 向列表追加一个指针。返回一个指向修改后列表的指针。
 * 注意，此函数可能会或可能不会对列表进行破坏性修改；
 * 调用者应始终使用此函数的返回值，而不是继续使用作为第一个参数传递的指针。
 */
List * lappend(List *fc_list, void *fc_datum)
{
	Assert(IsPointerList(fc_list));

	if (fc_list == NIL)
		fc_list = fc_new_list(T_List, 1);
	else
		fc_new_tail_cell(fc_list);

	llast(fc_list) = fc_datum;
	fc_check_list_invariants(fc_list);
	return fc_list;
}

/*
 * 向指定列表追加一个整数。见 lappend()
 */
List * lappend_int(List *fc_list, int fc_datum)
{
	Assert(IsIntegerList(fc_list));

	if (fc_list == NIL)
		fc_list = fc_new_list(T_IntList, 1);
	else
		fc_new_tail_cell(fc_list);

	llast_int(fc_list) = fc_datum;
	fc_check_list_invariants(fc_list);
	return fc_list;
}

/*
 * 向指定列表追加一个 OID。见 lappend()
 */
List * lappend_oid(List *fc_list, Oid fc_datum)
{
	Assert(IsOidList(fc_list));

	if (fc_list == NIL)
		fc_list = fc_new_list(T_OidList, 1);
	else
		fc_new_tail_cell(fc_list);

	llast_oid(fc_list) = fc_datum;
	fc_check_list_invariants(fc_list);
	return fc_list;
}

/*
 * 在位置“pos”（从0开始计）为一个新单元腾出空间。
 * 单元中的数据未定义，必须由调用者填充。“list”假定为非 NIL，
 * “pos”必须是有效的列表位置，即 0 <= pos <= 列表的长度。
 * 返回新单元的地址。
 */
static ListCell *
insert_new_cell(List *fc_list, int fc_pos)
{
	Assert(fc_pos >= 0 && fc_pos <= fc_list->length);

	/* 如有必要扩展数组 */
	if (fc_list->length >= fc_list->max_length)
		fc_enlarge_list(fc_list, fc_list->length + 1);
	/* 现在将现有数据移动过来 */
	if (fc_pos < fc_list->length)
		memmove(&fc_list->elements[fc_pos + 1], &fc_list->elements[fc_pos],
				(fc_list->length - fc_pos) * sizeof(ListCell));
	fc_list->length++;

	return &fc_list->elements[fc_pos];
}

/*
 * 在列表中位置“pos”（从0开始计）插入给定数据。
 * “pos”必须有效，即 0 <= pos <= 列表的长度。
 *
 * 注意，这个操作的时间与到列表末尾的距离成正比，
 * 因为后续条目必须移动。
 */
List * list_insert_nth(List *fc_list, int fc_pos, void *fc_datum)
{
	if (fc_list == NIL)
	{
		Assert(fc_pos == 0);
		return list_make1(fc_datum);
	}
	Assert(IsPointerList(fc_list));
	lfirst(insert_new_cell(fc_list, fc_pos)) = fc_datum;
	fc_check_list_invariants(fc_list);
	return fc_list;
}

List * list_insert_nth_int(List *fc_list, int fc_pos, int fc_datum)
{
	if (fc_list == NIL)
	{
		Assert(fc_pos == 0);
		return list_make1_int(fc_datum);
	}
	Assert(IsIntegerList(fc_list));
	lfirst_int(insert_new_cell(fc_list, fc_pos)) = fc_datum;
	fc_check_list_invariants(fc_list);
	return fc_list;
}

List * list_insert_nth_oid(List *fc_list, int fc_pos, Oid fc_datum)
{
	if (fc_list == NIL)
	{
		Assert(fc_pos == 0);
		return list_make1_oid(fc_datum);
	}
	Assert(IsOidList(fc_list));
	lfirst_oid(insert_new_cell(fc_list, fc_pos)) = fc_datum;
	fc_check_list_invariants(fc_list);
	return fc_list;
}

/*
 * 在列表前添加一个新元素。返回一个指向修改后列表的指针。
 * 注意，此函数可能会或可能不会对列表进行破坏性修改；
 * 调用者应始终使用此函数的返回值，而不是继续使用作为第二个参数传递的指针。
 *
 * 注意，这个操作的时间与列表的长度成正比，
 * 因为必须移动现有条目。
 *
 * 注意：在 Postgres 8.0 之前，原始列表未被修改，
 * 可以被认为保留其单独的身份。现在不再如此。
 */
List * lcons(void *fc_datum, List *fc_list)
{
	Assert(IsPointerList(fc_list));

	if (fc_list == NIL)
		fc_list = fc_new_list(T_List, 1);
	else
		fc_new_head_cell(fc_list);

	linitial(fc_list) = fc_datum;
	fc_check_list_invariants(fc_list);
	return fc_list;
}

/*
 * 在列表前添加一个整数。见 lcons()
 */
List * lcons_int(int fc_datum, List *fc_list)
{
	Assert(IsIntegerList(fc_list));

	if (fc_list == NIL)
		fc_list = fc_new_list(T_IntList, 1);
	else
		fc_new_head_cell(fc_list);

	linitial_int(fc_list) = fc_datum;
	fc_check_list_invariants(fc_list);
	return fc_list;
}

/*
 * 在列表前添加一个 OID。见 lcons()
 */
List * lcons_oid(Oid fc_datum, List *fc_list)
{
	Assert(IsOidList(fc_list));

	if (fc_list == NIL)
		fc_list = fc_new_list(T_OidList, 1);
	else
		fc_new_head_cell(fc_list);

	linitial_oid(fc_list) = fc_datum;
	fc_check_list_invariants(fc_list);
	return fc_list;
}


/*
 * 将list2连接到list1的末尾，并返回list1。
 *
 * 这相当于将list2的每个元素按顺序lappend到list1。
 * list1会被破坏性地更改，list2不会被更改。(然而，在指针列表的情况下，list1和list2将指向相同的结构。)
 *
 * 调用者应确保使用返回值作为连接列表的新指针：'list1'输入指针可能与返回的指针相同，也可能不同。
 *
 * 请注意，这至少需要与list2的长度成比例的时间。
 * 通常情况下，我们需要扩大list1的存储，可能会增加与list1长度成比例的时间。
 */
List * list_concat(List *fc_list1, const List *fc_list2)
{
	int			fc_new_len;

	if (fc_list1 == NIL)
		return list_copy(fc_list2);
	if (fc_list2 == NIL)
		return fc_list1;

	Assert(fc_list1->type == fc_list2->type);

	fc_new_len = fc_list1->length + fc_list2->length;
	/* 如有必要扩展数组 */
	if (fc_new_len > fc_list1->max_length)
		fc_enlarge_list(fc_list1, fc_new_len);

	/* 即使list1 == list2，这里的memcpy也应该是安全的 */
	memcpy(&fc_list1->elements[fc_list1->length], &fc_list2->elements[0],
		   fc_list2->length * sizeof(ListCell));
	fc_list1->length = fc_new_len;

	fc_check_list_invariants(fc_list1);
	return fc_list1;
}

/*
 * 通过连接list1和list2的元素形成一个新列表。
 *
 * 两个输入列表都没有被修改。(然而，如果它们是指针列表，输出列表将指向相同的结构。)
 *
 * 这与list_concat(list_copy(list1), list2)等效，但更高效。
 * 请注意，一些早于v13的代码可能也会列出list_copy list2，但现在这样做没有意义。
 */
List * list_concat_copy(const List *fc_list1, const List *fc_list2)
{
	List	   *fc_result;
	int			fc_new_len;

	if (fc_list1 == NIL)
		return list_copy(fc_list2);
	if (fc_list2 == NIL)
		return list_copy(fc_list1);

	Assert(fc_list1->type == fc_list2->type);

	fc_new_len = fc_list1->length + fc_list2->length;
	fc_result = fc_new_list(fc_list1->type, fc_new_len);
	memcpy(fc_result->elements, fc_list1->elements,
		   fc_list1->length * sizeof(ListCell));
	memcpy(fc_result->elements + fc_list1->length, fc_list2->elements,
		   fc_list2->length * sizeof(ListCell));

	fc_check_list_invariants(fc_result);
	return fc_result;
}

/*
 * 将'list'截断，只包含不超过'new_size'个元素。此
 * 函数会就地修改列表！尽管如此，调用者应该使用此函数返回的指针来引用新截断的
 * 列表——它可能与传入的指针相同，也可能不同。
 *
 * 请注意，通过list_truncate()移除的任何单元格都不会被pfree。 
 */
List * list_truncate(List *fc_list, int fc_new_size)
{
	if (fc_new_size <= 0)
		return NIL;				/* 截断为零长度 */

	/* 如果被要求有效地扩展列表，则不执行任何操作 */
	if (fc_new_size < list_length(fc_list))
		fc_list->length = fc_new_size;

	/*
	 * 注意：与单个列表单元删除函数不同，我们不会将
	 * 列表单元移动到新的存储，即使在 DEBUG_LIST_MEMORY_USAGE 模式下。
	 * 这是因为这些操作中没有任何一个可以移动，就像
	 * 在旧的 cons-cell 基于实现中一样，该函数不会
	 * 使任何指向列表单元的指针失效。这也是不清空已删除单元内存的原因：
	 * 老代码也没有释放它们。也许我们稍后会收紧这一点。
	 */

	return fc_list;
}

/*
 * 仅当 'datum' 是列表的成员时返回 true。相等性通过
 * equal() 确定，因此调用者应确保他们传递一个
 * Node 作为 'datum'。
 *
 * 这进行简单的线性搜索——避免在长列表上使用它。
 */
bool list_member(const List *fc_list, const void *fc_datum)
{
	const ListCell *fc_cell;

	Assert(IsPointerList(fc_list));
	fc_check_list_invariants(fc_list);

	foreach(fc_cell, fc_list)
	{
		if (equal(lfirst(fc_cell), fc_datum))
			return true;
	}

	return false;
}

/*
 * 仅当 'datum' 是列表的成员时返回 true。相等性
 * 通过简单的指针比较确定。
 */
bool list_member_ptr(const List *fc_list, const void *fc_datum)
{
	const ListCell *fc_cell;

	Assert(IsPointerList(fc_list));
	fc_check_list_invariants(fc_list);

	foreach(fc_cell, fc_list)
	{
		if (lfirst(fc_cell) == fc_datum)
			return true;
	}

	return false;
}

/*
 * 仅当整数 'datum' 是列表的成员时返回 true。
 */
bool list_member_int(const List *fc_list, int fc_datum)
{
	const ListCell *fc_cell;

	Assert(IsIntegerList(fc_list));
	fc_check_list_invariants(fc_list);

	foreach(fc_cell, fc_list)
	{
		if (lfirst_int(fc_cell) == fc_datum)
			return true;
	}

	return false;
}

/*
 * 仅当 OID 'datum' 是列表的成员时返回 true。
 */
bool list_member_oid(const List *fc_list, Oid fc_datum)
{
	const ListCell *fc_cell;

	Assert(IsOidList(fc_list));
	fc_check_list_invariants(fc_list);

	foreach(fc_cell, fc_list)
	{
		if (lfirst_oid(fc_cell) == fc_datum)
			return true;
	}

	return false;
}

/*
 * 删除列表中第 n 个单元（从 0 开始计数）。
 *
 * 如果这是最后一个成员，则列表将被 pfree。
 *
 * 注意，这花费的时间与距离列表末尾的距离成正比，
 * 因为后续条目必须移动。
 */
List * list_delete_nth_cell(List *fc_list, int fc_n)
{
	fc_check_list_invariants(fc_list);

	Assert(fc_n >= 0 && fc_n < fc_list->length);

	/*
	 * 如果我们即将从列表中删除最后一个节点，则释放整个
	 * 列表并返回 NIL，这是零长度列表的唯一有效表示。
	 */
	if (fc_list->length == 1)
	{
		list_free(fc_list);
		return NIL;
	}

	/*
	 * 否则，我们通常只是 collapse 出已删除的元素。但出于
	 * 调试目的，将整个列表内容移动到其他地方。
	 *
	 * （请注意，我们 *必须* 保持内容在相同的内存上下文中。）
	 */
#ifndef DEBUG_LIST_MEMORY_USAGE
	memmove(&fc_list->elements[fc_n], &fc_list->elements[fc_n + 1],
			(fc_list->length - 1 - fc_n) * sizeof(ListCell));
	fc_list->length--;
#else
	{
		ListCell   *fc_newelems;
		int			fc_newmaxlen = fc_list->length - 1;

		fc_newelems = (ListCell *)
			MemoryContextAlloc(GetMemoryChunkContext(fc_list),
							   fc_newmaxlen * sizeof(ListCell));
		memcpy(fc_newelems, fc_list->elements, fc_n * sizeof(ListCell));
		memcpy(&fc_newelems[fc_n], &fc_list->elements[fc_n + 1],
			   (fc_list->length - 1 - fc_n) * sizeof(ListCell));
		if (fc_list->elements != fc_list->initial_elements)
			pfree(fc_list->elements);
		else
		{
			/*
			 * 与 enlarge_list() 一样，清除 initial_elements[] 空间和/或
			 * 将其标记为不可访问。
			 */
#ifdef CLOBBER_FREED_MEMORY
			wipe_mem(fc_list->initial_elements,
					 fc_list->max_length * sizeof(ListCell));
#else
			VALGRIND_MAKE_MEM_NOACCESS(fc_list->initial_elements,
									   fc_list->max_length * sizeof(ListCell));
#endif
		}
		fc_list->elements = fc_newelems;
		fc_list->max_length = fc_newmaxlen;
		fc_list->length--;
		fc_check_list_invariants(fc_list);
	}
#endif

	return fc_list;
}

/*
 * 从 'list' 中删除 'cell'。
 *
 * 如果这是最后一个成员，则列表将被 pfree。然而，我们不会
 * 触及单元可能指向的任何数据。
 *
 * 注意，这花费的时间与距离列表末尾的距离成正比，
 * 因为后续条目必须移动。
 */
List * list_delete_cell(List *fc_list, ListCell *fc_cell)
{
	return list_delete_nth_cell(fc_list, fc_cell - fc_list->elements);
}

/*
 * 删除列表中与 datum 匹配的第一个单元（如果有）。
 * 相等性通过 equal() 确定。
 *
 * 这进行简单的线性搜索——避免在长列表上使用它。
 */
List * list_delete(List *fc_list, void *fc_datum)
{
	ListCell   *fc_cell;

	Assert(IsPointerList(fc_list));
	fc_check_list_invariants(fc_list);

	foreach(fc_cell, fc_list)
	{
		if (equal(lfirst(fc_cell), fc_datum))
			return list_delete_cell(fc_list, fc_cell);
	}

	/* 未找到匹配项：返回未修改的列表 */
	return fc_list;
}

/* 与上述相同，但使用简单的指针相等 */
List * list_delete_ptr(List *fc_list, void *fc_datum)
{
	ListCell   *fc_cell;

	Assert(IsPointerList(fc_list));
	fc_check_list_invariants(fc_list);

	foreach(fc_cell, fc_list)
	{
		if (lfirst(fc_cell) == fc_datum)
			return list_delete_cell(fc_list, fc_cell);
	}

	/* 未找到匹配项：返回未修改的列表 */
	return fc_list;
}

/* 与上述相同，但适用于整数 */
List * list_delete_int(List *fc_list, int fc_datum)
{
	ListCell   *fc_cell;

	Assert(IsIntegerList(fc_list));
	fc_check_list_invariants(fc_list);

	foreach(fc_cell, fc_list)
	{
		if (lfirst_int(fc_cell) == fc_datum)
			return list_delete_cell(fc_list, fc_cell);
	}

	/* 未找到匹配项：返回未修改的列表 */
	return fc_list;
}

/* 与上述相同，但适用于 OIDs */
List * list_delete_oid(List *fc_list, Oid fc_datum)
{
	ListCell   *fc_cell;

	Assert(IsOidList(fc_list));
	fc_check_list_invariants(fc_list);

	foreach(fc_cell, fc_list)
	{
		if (lfirst_oid(fc_cell) == fc_datum)
			return list_delete_cell(fc_list, fc_cell);
	}

	/* 未找到匹配项：返回未修改的列表 */
	return fc_list;
}

/*
 * 删除列表的第一个元素。
 *
 * 这对于替换 Lisp 风格的代码 "list = lnext(list);" 很有用，尤其是在
 * 意图是修改列表而不仅仅是遍历它的情况下。
 * 注意，列表被修改，而 Lisp 风格的编码会保持
 * 原始列表头不变，以防有另一个指针指向它。
 *
 * 注意，这花费的时间与列表的长度成正比，
 * 因为剩余条目必须移动。考虑反转
 * 列表顺序，以便你可以使用 list_delete_last() 代替。然而，
 * 如果这导致你用 lcons() 替换 lappend()，那么你并没有改善
 * 问题。（简而言之，你可以从
 * List 创建一个有效的堆栈，但不能创建一个有效的 FIFO 队列。）
 */
List * list_delete_first(List *fc_list)
{
	fc_check_list_invariants(fc_list);

	if (fc_list == NIL)
		return NIL;				/* 错误会更好吗？ */

	return list_delete_nth_cell(fc_list, 0);
}

/*
 * 删除列表的最后一个元素。
 */
List * list_delete_last(List *fc_list)
{
	fc_check_list_invariants(fc_list);

	if (fc_list == NIL)
		return NIL;				/* 错误会更好吗？ */

	/* list_truncate 不会在列表变为空时释放列表，但这应该会 */
	if (list_length(fc_list) <= 1)
	{
		list_free(fc_list);
		return NIL;
	}

	return list_truncate(fc_list, list_length(fc_list) - 1);
}

/*
 * 删除列表中的前 N 个单元。
 *
 * 如果请求导致所有单元被删除，则列表将被 pfree。
 *
 * 注意，这花费的时间与距离列表末尾的距离成正比，
 * 因为后续条目必须移动。
 */
List * list_delete_first_n(List *fc_list, int fc_n)
{
	fc_check_list_invariants(fc_list);

	/* 无操作请求？ */
	if (fc_n <= 0)
		return fc_list;

	/* 删除整个列表？ */
	if (fc_n >= list_length(fc_list))
	{
		list_free(fc_list);
		return NIL;
	}

	/*
	 * 否则，我们通常只是 collapse 出已删除的元素。但出于
	 * 调试目的，将整个列表内容移动到其他地方。
	 *
	 * （请注意，我们 *必须* 保持内容在相同的内存上下文中。）
	 */
#ifndef DEBUG_LIST_MEMORY_USAGE
	memmove(&fc_list->elements[0], &fc_list->elements[fc_n],
			(fc_list->length - fc_n) * sizeof(ListCell));
	fc_list->length -= fc_n;
#else
	{
		ListCell   *fc_newelems;
		int			fc_newmaxlen = fc_list->length - fc_n;

		fc_newelems = (ListCell *)
			MemoryContextAlloc(GetMemoryChunkContext(fc_list),
							   fc_newmaxlen * sizeof(ListCell));
		memcpy(fc_newelems, &fc_list->elements[fc_n], fc_newmaxlen * sizeof(ListCell));
		if (fc_list->elements != fc_list->initial_elements)
			pfree(fc_list->elements);
		else
		{
			/*
			 * 与 enlarge_list() 一样，清除 initial_elements[] 空间和/或
			 * 将其标记为不可访问。
			 */
#ifdef CLOBBER_FREED_MEMORY
			wipe_mem(fc_list->initial_elements,
					 fc_list->max_length * sizeof(ListCell));
#else
			VALGRIND_MAKE_MEM_NOACCESS(list->initial_elements,
									   list->max_length * sizeof(ListCell));
#endif
		}
		fc_list->elements = fc_newelems;
		fc_list->max_length = fc_newmaxlen;
		fc_list->length = fc_newmaxlen;	
		fc_check_list_invariants(fc_list);
	}
#endif

	return fc_list;
}


/*
 * 生成两个列表的联合。这是通过使用 list_copy() 复制 list1，
 * 然后将 list2 中所有不在 list1 中的成员添加到其上来计算的。
 *
 * 元素是否已经是列表的成员通过 equal() 确定。
 *
 * 返回的列表是新分配的，尽管单元的内容是相同的（即任何指向的对象不被复制）。
 *
 * 注意：此函数不会移除 list1 中存在的任何重复项（因此仅在
 * list1 已知唯一时执行“联合”）。此外，如果您即将写“x = list_union(x, y)”
 * 您可能想要使用 list_concat_unique() 来避免浪费旧 x 列表的存储。
 *
 * 请注意，这会消耗与列表长度的乘积成比例的时间，因此使用于长列表时要当心。
 * （我们可能能够改进这一点，但如果这成为性能瓶颈，您真的应该使用其他数据结构。）
 */
List * list_union(const List *fc_list1, const List *fc_list2)
{
	List	   *fc_result;
	const ListCell *fc_cell;

	Assert(IsPointerList(fc_list1));
	Assert(IsPointerList(fc_list2));

	fc_result = list_copy(fc_list1);
	foreach(fc_cell, fc_list2)
	{
		if (!list_member(fc_result, lfirst(fc_cell)))
			fc_result = lappend(fc_result, lfirst(fc_cell));
	}

	fc_check_list_invariants(fc_result);
	return fc_result;
}

/*
 * 这个 list_union() 的变体通过简单的指针比较来确定重复项。
 */
List * list_union_ptr(const List *fc_list1, const List *fc_list2)
{
	List	   *fc_result;
	const ListCell *fc_cell;

	Assert(IsPointerList(fc_list1));
	Assert(IsPointerList(fc_list2));

	fc_result = list_copy(fc_list1);
	foreach(fc_cell, fc_list2)
	{
		if (!list_member_ptr(fc_result, lfirst(fc_cell)))
			fc_result = lappend(fc_result, lfirst(fc_cell));
	}

	fc_check_list_invariants(fc_result);
	return fc_result;
}

/*
 * 这个 list_union() 的变体操作于整数列表。
 */
List * list_union_int(const List *fc_list1, const List *fc_list2)
{
	List	   *fc_result;
	const ListCell *fc_cell;

	Assert(IsIntegerList(fc_list1));
	Assert(IsIntegerList(fc_list2));

	fc_result = list_copy(fc_list1);
	foreach(fc_cell, fc_list2)
	{
		if (!list_member_int(fc_result, lfirst_int(fc_cell)))
			fc_result = lappend_int(fc_result, lfirst_int(fc_cell));
	}

	fc_check_list_invariants(fc_result);
	return fc_result;
}

/*
 * 这个 list_union() 的变体操作于 OID 列表。
 */
List * list_union_oid(const List *fc_list1, const List *fc_list2)
{
	List	   *fc_result;
	const ListCell *fc_cell;

	Assert(IsOidList(fc_list1));
	Assert(IsOidList(fc_list2));

	fc_result = list_copy(fc_list1);
	foreach(fc_cell, fc_list2)
	{
		if (!list_member_oid(fc_result, lfirst_oid(fc_cell)))
			fc_result = lappend_oid(fc_result, lfirst_oid(fc_cell));
	}

	fc_check_list_invariants(fc_result);
	return fc_result;
}

/*
 * 返回一个包含 list1 和 list2 中所有单元的列表。返回的列表通过 palloc() 新分配，
 * 但单元本身指向与输入列表中的单元相同的对象。
 *
 * list1 中的重复条目将不会被压制，因此如果事先已知 list1 是唯一的，
 * 那么这才是真正的“交集”。
 *
 * 这个变体在指针列表上工作，并通过 equal() 确定列表成员资格。
 * 请注意，结果中会指向 list1 的成员。
 *
 * 请注意，这会消耗与列表长度的乘积成比例的时间，因此使用于长列表时要当心。
 * （我们可能能够改进这一点，但如果这成为性能瓶颈，您真的应该使用其他数据结构。）
 */
List * list_intersection(const List *fc_list1, const List *fc_list2)
{
	List	   *fc_result;
	const ListCell *fc_cell;

	if (fc_list1 == NIL || fc_list2 == NIL)
		return NIL;

	Assert(IsPointerList(fc_list1));
	Assert(IsPointerList(fc_list2));

	fc_result = NIL;
	foreach(fc_cell, fc_list1)
	{
		if (list_member(fc_list2, lfirst(fc_cell)))
			fc_result = lappend(fc_result, lfirst(fc_cell));
	}

	fc_check_list_invariants(fc_result);
	return fc_result;
}

/*
 * 与 list_intersection 相似，但操作整数列表。
 */
List * list_intersection_int(const List *fc_list1, const List *fc_list2)
{
	List	   *fc_result;
	const ListCell *fc_cell;

	if (fc_list1 == NIL || fc_list2 == NIL)
		return NIL;

	Assert(IsIntegerList(fc_list1));
	Assert(IsIntegerList(fc_list2));

	fc_result = NIL;
	foreach(fc_cell, fc_list1)
	{
		if (list_member_int(fc_list2, lfirst_int(fc_cell)))
			fc_result = lappend_int(fc_result, lfirst_int(fc_cell));
	}

	fc_check_list_invariants(fc_result);
	return fc_result;
}

/*
 * 返回一个列表，包含 list1 中不在 list2 中的所有单元。返回的列表通过 palloc() 新分配，但单元本身指向与输入列表单元相同的对象。
 *
 * 此变体在指针列表上工作，通过 equal() 确定列表成员关系。
 *
 * 请注意，这需要的时间与列表长度的乘积成正比，因此请注意在长列表上使用它。 （我们可能可以改进这一点，但如果这是性能瓶颈，您真的应该使用其他数据结构。）
 */
List * list_difference(const List *fc_list1, const List *fc_list2)
{
	const ListCell *fc_cell;
	List	   *fc_result = NIL;

	Assert(IsPointerList(fc_list1));
	Assert(IsPointerList(fc_list2));

	if (fc_list2 == NIL)
		return list_copy(fc_list1);

	foreach(fc_cell, fc_list1)
	{
		if (!list_member(fc_list2, lfirst(fc_cell)))
			fc_result = lappend(fc_result, lfirst(fc_cell));
	}

	fc_check_list_invariants(fc_result);
	return fc_result;
}

/*
 * 此 list_difference() 的变体通过简单的指针相等性来确定列表成员关系。
 */
List * list_difference_ptr(const List *fc_list1, const List *fc_list2)
{
	const ListCell *fc_cell;
	List	   *fc_result = NIL;

	Assert(IsPointerList(fc_list1));
	Assert(IsPointerList(fc_list2));

	if (fc_list2 == NIL)
		return list_copy(fc_list1);

	foreach(fc_cell, fc_list1)
	{
		if (!list_member_ptr(fc_list2, lfirst(fc_cell)))
			fc_result = lappend(fc_result, lfirst(fc_cell));
	}

	fc_check_list_invariants(fc_result);
	return fc_result;
}

/*
 * 此 list_difference() 的变体在整数列表上操作。
 */
List * list_difference_int(const List *fc_list1, const List *fc_list2)
{
	const ListCell *fc_cell;
	List	   *fc_result = NIL;

	Assert(IsIntegerList(fc_list1));
	Assert(IsIntegerList(fc_list2));

	if (fc_list2 == NIL)
		return list_copy(fc_list1);

	foreach(fc_cell, fc_list1)
	{
		if (!list_member_int(fc_list2, lfirst_int(fc_cell)))
			fc_result = lappend_int(fc_result, lfirst_int(fc_cell));
	}

	fc_check_list_invariants(fc_result);
	return fc_result;
}

/*
 * 此 list_difference() 的变体在 OID 列表上操作。
 */
List * list_difference_oid(const List *fc_list1, const List *fc_list2)
{
	const ListCell *fc_cell;
	List	   *fc_result = NIL;

	Assert(IsOidList(fc_list1));
	Assert(IsOidList(fc_list2));

	if (fc_list2 == NIL)
		return list_copy(fc_list1);

	foreach(fc_cell, fc_list1)
	{
		if (!list_member_oid(fc_list2, lfirst_oid(fc_cell)))
			fc_result = lappend_oid(fc_result, lfirst_oid(fc_cell));
	}

	fc_check_list_invariants(fc_result);
	return fc_result;
}

/*
 * 将 datum 附加到列表，但前提是它尚未在列表中。
 *
 * 元素是否已经是列表的一部分是通过 equal() 来确定的。
 *
 * 这进行简单的线性搜索——避免在长列表上使用它。
 */
List * list_append_unique(List *fc_list, void *fc_datum)
{
	if (list_member(fc_list, fc_datum))
		return fc_list;
	else
		return lappend(fc_list, fc_datum);
}

/*
 * 此 list_append_unique() 的变体通过简单的指针相等性来确定列表成员关系。
 */
List * list_append_unique_ptr(List *fc_list, void *fc_datum)
{
	if (list_member_ptr(fc_list, fc_datum))
		return fc_list;
	else
		return lappend(fc_list, fc_datum);
}

/*
 * 此 list_append_unique() 的变体在整数列表上操作。
 */
List * list_append_unique_int(List *fc_list, int fc_datum)
{
	if (list_member_int(fc_list, fc_datum))
		return fc_list;
	else
		return lappend_int(fc_list, fc_datum);
}

/*
 * 此 list_append_unique() 的变体在 OID 列表上操作。
 */
List * list_append_unique_oid(List *fc_list, Oid fc_datum)
{
	if (list_member_oid(fc_list, fc_datum))
		return fc_list;
	else
		return lappend_oid(fc_list, fc_datum);
}

/*
 * 将 list2 中每个不在 list1 中的成员附加到 list1。
 *
 * 元素是否已经是列表的一部分是通过 equal() 来确定的。
 *
 * 这几乎与 list_union() 具有相同的功能，但 list1 被就地修改，而不是被复制。然而，此函数的调用者可能有严格的排序期望——即，未重复的 list2 元素的相对顺序被保留。
 *
 * 请注意，这需要的时间与列表长度的乘积成正比，因此请注意在长列表上使用它。 （我们可能可以改进这一点，但如果这是性能瓶颈，您真的应该使用其他数据结构。）
 */
List * list_concat_unique(List *fc_list1, const List *fc_list2)
{
	ListCell   *fc_cell;

	Assert(IsPointerList(fc_list1));
	Assert(IsPointerList(fc_list2));

	foreach(fc_cell, fc_list2)
	{
		if (!list_member(fc_list1, lfirst(fc_cell)))
			fc_list1 = lappend(fc_list1, lfirst(fc_cell));
	}

	fc_check_list_invariants(fc_list1);
	return fc_list1;
}

/*
 * 此 list_concat_unique() 的变体通过简单的指针相等性来确定列表成员关系。
 */
List * list_concat_unique_ptr(List *fc_list1, const List *fc_list2)
{
	ListCell   *fc_cell;

	Assert(IsPointerList(fc_list1));
	Assert(IsPointerList(fc_list2));

	foreach(fc_cell, fc_list2)
	{
		if (!list_member_ptr(fc_list1, lfirst(fc_cell)))
			fc_list1 = lappend(fc_list1, lfirst(fc_cell));
	}

	fc_check_list_invariants(fc_list1);
	return fc_list1;
}

/*
 * 此 list_concat_unique() 的变体在整数列表上操作。
 */
List * list_concat_unique_int(List *fc_list1, const List *fc_list2)
{
	ListCell   *fc_cell;

	Assert(IsIntegerList(fc_list1));
	Assert(IsIntegerList(fc_list2));

	foreach(fc_cell, fc_list2)
	{
		if (!list_member_int(fc_list1, lfirst_int(fc_cell)))
			fc_list1 = lappend_int(fc_list1, lfirst_int(fc_cell));
	}

	fc_check_list_invariants(fc_list1);
	return fc_list1;
}

/*
 * 此 list_concat_unique() 的变体在 OID 列表上操作。
 */
List * list_concat_unique_oid(List *fc_list1, const List *fc_list2)
{
	ListCell   *fc_cell;

	Assert(IsOidList(fc_list1));
	Assert(IsOidList(fc_list2));

	foreach(fc_cell, fc_list2)
	{
		if (!list_member_oid(fc_list1, lfirst_oid(fc_cell)))
			fc_list1 = lappend_oid(fc_list1, lfirst_oid(fc_cell));
	}

	fc_check_list_invariants(fc_list1);
	return fc_list1;
}

/*
 * 移除 OID 列表中的相邻重复项。
 *
 * 确保列表已排序，以将重复项靠近，可能通过 list_sort(list, list_oid_cmp)。
 *
 * 请注意，这需要的时间与列表的长度成正比。
 */
void list_deduplicate_oid(List *fc_list)
{
	int			fc_len;

	Assert(IsOidList(fc_list));
	fc_len = list_length(fc_list);
	if (fc_len > 1)
	{
		ListCell   *fc_elements = fc_list->elements;
		int			fc_i = 0;

		for (int fc_j = 1; fc_j < fc_len; fc_j++)
		{
			if (fc_elements[fc_i].oid_value != fc_elements[fc_j].oid_value)
				fc_elements[++fc_i].oid_value = fc_elements[fc_j].oid_value;
		}
		fc_list->length = fc_i + 1;
	}
	fc_check_list_invariants(fc_list);
}

/*
 * 释放列表中的所有存储，并可选择释放指向的元素。
 */
static void fc_list_free_private(List *fc_list, bool fc_deep)
{
	if (fc_list == NIL)
		return;					/* 无需操作 */

	fc_check_list_invariants(fc_list);

	if (fc_deep)
	{
		for (int fc_i = 0; fc_i < fc_list->length; fc_i++)
			pfree(lfirst(&fc_list->elements[fc_i]));
	}
	if (fc_list->elements != fc_list->initial_elements)
		pfree(fc_list->elements);
	pfree(fc_list);
}

/*
 * 释放列表的所有单元，以及列表本身。列表单元指向的任何对象都不会被释放。
 *
 * 返回时，此函数的参数已被释放，因此调用者最好将其设为 NIL 以确保安全。
 */
void list_free(List *fc_list)
{
	fc_list_free_private(fc_list, false);
}

/*
 * 释放列表的所有单元、列表本身以及列表单元指向的所有对象（列表中的每个元素必须包含指向 palloc() 分配内存区域的指针！）
 *
 * 返回时，此函数的参数已被释放，因此调用者最好将其设为 NIL 以确保安全。
 */
void list_free_deep(List *fc_list)
{
	/*
	 * "深度"释放操作仅对指针列表有意义。
	 */
	Assert(IsPointerList(fc_list));
	fc_list_free_private(fc_list, true);
}

/*
 * 返回指定列表的浅拷贝。
 */
List * list_copy(const List *fc_oldlist)
{
	List	   *fc_newlist;

	if (fc_oldlist == NIL)
		return NIL;

	fc_newlist = fc_new_list(fc_oldlist->type, fc_oldlist->length);
	memcpy(fc_newlist->elements, fc_oldlist->elements,
		   fc_newlist->length * sizeof(ListCell));

	fc_check_list_invariants(fc_newlist);
	return fc_newlist;
}

/*
 * 返回指定列表的浅拷贝，仅包含前 'len' 个元素。如果旧列表短于 'len'，则我们复制整个列表。
 */
List * list_copy_head(const List *fc_oldlist, int fc_len)
{
	List	   *fc_newlist;

	if (fc_oldlist == NIL || fc_len <= 0)
		return NIL;

	fc_len = Min(fc_oldlist->length, fc_len);

	fc_newlist = fc_new_list(fc_oldlist->type, fc_len);
	memcpy(fc_newlist->elements, fc_oldlist->elements, fc_len * sizeof(ListCell));

	fc_check_list_invariants(fc_newlist);
	return fc_newlist;
}

/*
 * 返回指定列表的浅拷贝，去除前 N 个元素。
 */
List * list_copy_tail(const List *fc_oldlist, int fc_nskip)
{
	List	   *fc_newlist;

	if (fc_nskip < 0)
		fc_nskip = 0;				/* 是否更好地记录？ */

	if (fc_oldlist == NIL || fc_nskip >= fc_oldlist->length)
		return NIL;

	fc_newlist = fc_new_list(fc_oldlist->type, fc_oldlist->length - fc_nskip);
	memcpy(fc_newlist->elements, &fc_oldlist->elements[fc_nskip],
		   fc_newlist->length * sizeof(ListCell));

	fc_check_list_invariants(fc_newlist);
	return fc_newlist;
}

/*
 * 返回指定列表的深拷贝。
 *
 * 列表元素通过 copyObject() 复制，因此此函数的“深度”复制概念远比 list_free_deep() 所说的要深得多。
 */
List * list_copy_deep(const List *fc_oldlist)
{
	List	   *fc_newlist;

	if (fc_oldlist == NIL)
		return NIL;

	/* 这仅对指针列表有意义 */
	Assert(IsA(fc_oldlist, List));

	fc_newlist = fc_new_list(fc_oldlist->type, fc_oldlist->length);
	for (int fc_i = 0; fc_i < fc_newlist->length; fc_i++)
		lfirst(&fc_newlist->elements[fc_i]) =
			copyObjectImpl(lfirst(&fc_oldlist->elements[fc_i]));

	fc_check_list_invariants(fc_newlist);
	return fc_newlist;
}

/*
 * 根据指定的比较函数对列表进行排序。
 *
 * 列表就地排序。
 *
 * 比较函数声明为接收 const ListCell * 类型的参数；这使其能够使用 lfirst() 和变体，而无需转换其参数。否则，它的行为与标准 qsort() 的比较函数相同。
 *
 * 像 qsort() 一样，这不保证对相等键的排序稳定性。
 *
 * 这是基于 qsort() 的，因此它同样具有 O(N log N) 的运行时间。
 */
void list_sort(List *fc_list, list_sort_comparator fc_cmp)
{
	typedef int (*qsort_comparator) (const void *a, const void *b);
	int			fc_len;

	fc_check_list_invariants(fc_list);

	/* 当元素少于两个时无需做任何事情 */
	fc_len = list_length(fc_list);
	if (fc_len > 1)
		qsort(fc_list->elements, fc_len, sizeof(ListCell), (qsort_comparator) fc_cmp);
}

/*
 * list_sort 比较器，用于将列表按升序整型排序。
 */
int list_int_cmp(const ListCell *fc_p1, const ListCell *fc_p2)
{
	int			fc_v1 = lfirst_int(fc_p1);
	int			fc_v2 = lfirst_int(fc_p2);

	if (fc_v1 < fc_v2)
		return -1;
	if (fc_v1 > fc_v2)
		return 1;
	return 0;
}


/*
 * list_sort 比较器，用于将列表排序为升序 OID。
 */
int list_oid_cmp(const ListCell *fc_p1, const ListCell *fc_p2)
{
	Oid			fc_v1 = lfirst_oid(fc_p1);
	Oid			fc_v2 = lfirst_oid(fc_p2);

	if (fc_v1 < fc_v2)
		return -1;
	if (fc_v1 > fc_v2)
		return 1;
	return 0;
}
